`include "mycpu.h"

module WB_stage(
    input                           clk           ,
    input                           reset         ,
    //allowin
    output                          ws_allowin    ,
    //from ms
    input                           ms_to_ws_valid,
    input  [`MS_TO_WS_BUS_WD -1:0]  ms_to_ws_bus  ,//70
    // input  [31:0]                   mfc0_out_data,
    //to rf: for write back
    output [`WS_TO_RF_BUS_WD -1:0]  ws_to_rf_bus  ,//38
    //to ds
    output [`_TO_DS_BUS_WD   -1:0]  ws_to_ds_bus,
    //to th (test_hazard)
    output [`_TO_TH_BUS_WD -1:0]   ws_to_th_bus,

    output                         ws_to_es_flush,
    input                          cr_flush_o,
    output                          ws_io_uart_out_valid,
    output                          ws_skip,
    ////
    output  [`WS_TO_CSR_BUS_WD -1:0] ws_to_csr_bus,
    input   [63:0]                  csr_rdata,
    //trace debug interface
    // (*mark_debug = "true"*) output [31:0] debug_wb_pc     ,
    // (*mark_debug = "true"*) output [ 3:0] debug_wb_rf_wen ,
    // (*mark_debug = "true"*) output [ 4:0] debug_wb_rf_wnum,
    // (*mark_debug = "true"*) output [31:0] debug_wb_rf_wdata

    output [63:0] debug_wb_pc     ,
    output [31:0] debug_wb_inst,
    output        debug_wb_valid,
    output        debug_wb_rf_wen ,
    output [ 4:0] debug_wb_rf_wnum,
    output [63:0] debug_wb_rf_wdata
    

);

reg         ws_valid;
reg [`MS_TO_WS_BUS_WD -1:0] ms_to_ws_bus_r;

wire        ws_ready_go;
wire        ws_gr_we;
wire        rf_we;
wire [4 :0] rf_waddr;
wire [4:0]  ws_dest;
wire [63:0] ws_final_result;
wire [63:0] ws_pc;
wire [63:0] ws_rt_value;
wire [63:0] rf_wdata;
wire [63:0] ws_rs1_value;

wire [31:0] ws_inst;
wire        ws_0x7b;
assign ws_io_uart_out_valid = ws_valid && ws_0x7b;
////csr////
wire[11:0] csr_addr;
wire[4:0]   ws_zimm;
wire        ws_int;
wire        ws_ex;
wire  ws_csrrw ; 
wire  ws_csrrs ;
wire  ws_csrrc ;
wire  ws_csrrwi;
wire  ws_csrrsi;
wire  ws_csrrci;
wire  ws_mret;
wire  ws_skip_clint;
wire  op_csr;
wire  csr_op;

wire   is_mstatus;
wire   is_mtvec;
wire   is_mepc;
wire   is_mcause;
wire   is_mip;
wire   is_mie;
wire   is_mcycle;
wire csrw_mstatus_xs_fs;
wire   illegal_csr;

assign illegal_csr = !(is_mstatus || is_mie || is_mtvec || is_mepc || is_mcause || is_mip || is_mcycle);
assign is_mstatus  = csr_addr == 12'h300;
assign is_mie      = csr_addr == 12'h304;
assign is_mtvec    = csr_addr == 12'h305;
assign is_mepc     = csr_addr == 12'h341;
assign is_mcause   = csr_addr == 12'h342;
assign is_mip      = csr_addr == 12'h344;
assign is_mcycle   = csr_addr == 12'hB00;


assign csrw_mstatus_xs_fs = is_mstatus &&  ws_csrrw |  ws_csrrs |  ws_csrrc && ws_valid 
                         && ws_rs1_value[14:13] != 2'b0 | ws_rs1_value[12:11] != 2'b0;
assign op_csr =  ws_csrrw   ||  ws_csrrs  ||  ws_csrrc  ||  ws_csrrwi ||  ws_csrrsi ||  ws_csrrci ;
assign ws_skip = (ws_skip_clint || ws_0x7b || op_csr && csr_addr == 12'hB00  || illegal_csr && op_csr) &&ws_valid;

assign {
                        ws_skip_clint,
                        ws_int,
                        ws_ex,
                        ws_mret,
                        ws_csrrw ,
                        ws_csrrs ,
                        ws_csrrc ,
                        ws_csrrwi,
                        ws_csrrsi,
                        ws_csrrci,                        
                        ws_zimm,
                        csr_addr,
       ws_0x7b,
       ws_gr_we       ,  //133:133
       ws_dest        ,  //132:128
       ws_rs1_value   ,
       ws_final_result,  //127:64
       ws_inst        ,
       ws_pc       //63:0
       } = ms_to_ws_bus_r;

assign ws_to_csr_bus = {

    ws_int,
    ws_ex,
    ws_valid,
    ws_mret,
    ws_csrrw ,
    ws_csrrs ,
    ws_csrrc ,
    ws_csrrwi,
    ws_csrrsi,
    ws_csrrci,                        
    ws_zimm,
    csr_addr,
    ws_pc,
    ws_rs1_value
};

assign ws_to_es_flush = (ws_ex || ws_int || ws_mret) && ws_valid;
assign ws_to_rf_bus = {rf_we   ,  //37:37
                       rf_waddr,  //36:32
                       rf_wdata   //31:0
                      };


assign ws_to_th_bus = { 
                        ws_gr_we  , //6:6
                        ws_valid , //5:5
                        ws_dest    //4:0
};
assign ws_to_ds_bus = {
                        ws_valid, 
                        ws_gr_we,
                        ws_dest,
                        ws_final_result
};

assign ws_ready_go = 1'b1;
assign ws_allowin  = !ws_valid || ws_ready_go;
always @(posedge clk) begin
    if (reset | cr_flush_o) begin
        ws_valid <= 1'b0;
    end
    else if (ws_allowin) begin
        ws_valid <= ms_to_ws_valid;
    end

    if (ms_to_ws_valid && ws_allowin) begin
        ms_to_ws_bus_r <= ms_to_ws_bus;
    end
end


assign csr_op =  ws_csrrw  || ws_csrrs || ws_csrrc || ws_csrrwi || ws_csrrsi || ws_csrrci;

assign rf_we    = ws_gr_we && ws_valid && !ws_ex;
assign rf_waddr = ws_dest;
assign rf_wdata = csr_op ? csr_rdata : ws_final_result;

// debug info generate
assign debug_wb_pc       = ws_pc;
assign debug_wb_rf_wen   = rf_we;
assign debug_wb_rf_wnum  = ws_dest;
assign debug_wb_rf_wdata = rf_wdata;
assign debug_wb_inst     = ws_inst;
assign debug_wb_valid    = ws_valid && !ws_ex;

endmodule
